package com.ng.framework.view.desktop.table;

import java.awt.Color;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.Hashtable;

public class NGCellAttributes {

	public static final int ROW = 0;
	public static final int COLUMN = 1;
	
	private int rows;
	private int columns;

	private int[][][] span;
	private boolean[][] editable;
	private Color[][] foreground;
	private Color[][] background;
	private Integer[][] alignment;
	private boolean[] fraction;
	private Hashtable<Class<? extends Number>, NumberFormat> numberFormats;

	public NGCellAttributes() {
		this(0, 0);
	}

	public NGCellAttributes(int rows, int columns) {
		this.numberFormats = new Hashtable<Class<? extends Number>, NumberFormat>();

		this.setSize(rows, columns);
	}

	private void setSize(int rows, int columns) {
		this.rows = rows;
		this.columns = columns;
		
		this.span = new int[rows][columns][2];
		this.editable = new boolean[rows][columns];
		this.foreground = new Color[rows][columns];
		this.background = new Color[rows][columns];
		this.alignment = new Integer[rows][columns];
		this.fraction = new boolean[columns];

		initValues();
	}

	private void initValues() {
		for (int i = 0; i < this.rows; i++) {
			for (int j = 0; j < this.columns; j++) {
				span[i][j][ROW] = 1;
				span[i][j][COLUMN] = 1;
				
				editable[i][j] = false;
			}
		}
		
		for(int i = 0; i < this.columns; i++) {
			fraction[i] = false;
		}
		
		numberFormats.put(Byte.class, new DecimalFormat("#"));
		numberFormats.put(Short.class, new DecimalFormat("#"));
		numberFormats.put(Integer.class, new DecimalFormat("#"));
		numberFormats.put(Long.class, new DecimalFormat("0.00"));
		numberFormats.put(Float.class, new DecimalFormat("0.00"));
		numberFormats.put(Double.class, new DecimalFormat("0.00"));
	}

	public int[] getSpan(int row, int column) {
		return span[row][column];
	}

	public void setSpan(int[] span, int row, int column) {
		this.span[row][column] = span;
	}

	public void combine(int[] rows, int[] columns) {
		int rowSpan = rows.length;
		int columnSpan = columns.length;

		int startRow = rows[0];
		int startColumn = columns[0];

		if (this.canCombine(startRow, startColumn, rowSpan, columnSpan)) {
			for (int i = 0, ii = 0; i < rowSpan; i++, ii--) {
				for (int j = 0, jj = 0; j < columnSpan; j++, jj--) {
					span[startRow + i][startColumn + j][COLUMN] = jj;
					span[startRow + i][startColumn + j][ROW] = ii;
				}
			}
			span[startRow][startColumn][COLUMN] = columnSpan;
			span[startRow][startColumn][ROW] = rowSpan;
		}

	}

	private boolean canCombine(int startRow, int startColumn, int rowSpan,
			int columnSpan) {
		boolean canCombine = true;

		for (int i = 0; i < rowSpan; i++) {
			for (int j = 0; j < columnSpan; j++) {
				if ((span[startRow + i][startColumn + j][COLUMN] != 1)
						|| (span[startRow + i][startColumn + j][ROW] != 1)) {
					canCombine = false;
				}
			}
		}

		return canCombine;
	}

	public void split(int row, int column) {
		int columnSpan = span[row][column][COLUMN];
		int rowSpan = span[row][column][ROW];

		for (int i = 0; i < rowSpan; i++) {
			for (int j = 0; j < columnSpan; j++) {
				span[row + i][column + j][COLUMN] = 1;
				span[row + i][column + j][ROW] = 1;
			}
		}
	}

	public void addColumn() {
		int[][][] oldSpan = span;
		boolean[][] oldEditable = editable;
		Color[][] oldForeground = foreground;
		Color[][] oldBackground = background;
		Integer[][] oldAlignment = alignment;
		boolean[] oldFraction = fraction;

		span = new int[rows][columns + 1][2];
		editable = new boolean[rows][columns + 1];
		foreground = new Color[rows][columns + 1];
		background = new Color[rows][columns + 1];
		alignment = new Integer[rows][columns + 1];
		fraction = new boolean[columns + 1];
		
		System.arraycopy(oldSpan, 0, span, 0, rows);
		System.arraycopy(oldEditable, 0, editable, 0, rows);
		System.arraycopy(oldForeground, 0, foreground, 0, rows);
		System.arraycopy(oldBackground, 0, background, 0, rows);
		System.arraycopy(oldAlignment, 0, alignment, 0, rows);
		System.arraycopy(oldFraction, 0, alignment, 0, columns + 1);

		for (int i = 0; i < rows; i++) {
			span[i][columns][COLUMN] = 1;
			span[i][columns][ROW] = 1;
		}
		
		columns++;
	}

	public void addRow() {
		int[][][] oldSpan = span;
		boolean[][] oldEditable = editable;
		Color[][] oldForeground = foreground;
		Color[][] oldBackground = background;
		Integer[][] oldAlignment = alignment;

		span = new int[rows + 1][columns][2];
		editable = new boolean[rows +1][columns];
		foreground = new Color[rows +1][columns];
		background = new Color[rows + 1][columns];
		alignment = new Integer[rows +1][columns];
		
		System.arraycopy(oldSpan, 0, span, 0, rows);
		System.arraycopy(oldEditable, 0, editable, 0, rows);
		System.arraycopy(oldForeground, 0, foreground, 0, rows);
		System.arraycopy(oldBackground, 0, background, 0, rows);
		System.arraycopy(oldAlignment, 0, alignment, 0, rows);

		for (int i = 0; i < columns; i++) {
			span[rows][i][COLUMN] = 1;
			span[rows][i][ROW] = 1;
			
			editable[rows][i] = false;
		}
		
		rows++;
	}

	public void insertRow(int row) {
		int[][][] oldSpan = span;
		boolean[][] oldEditable = editable;
		Color[][] oldForeground = foreground;
		Color[][] oldBackground = background;
		Integer[][] oldAlignment = alignment;

		span = new int[rows + 1][columns][2];
		editable = new boolean[rows + 1][columns];
		foreground = new Color[rows +1][columns];
		background = new Color[rows +1][columns];
		alignment = new Integer[rows +1][columns];
		
		if (row > 0) {
			System.arraycopy(oldSpan, 0, span, 0, row - 1);
			System.arraycopy(oldEditable, 0, editable, 0, row - 1);
			System.arraycopy(oldForeground, 0, foreground, 0, row - 1);
			System.arraycopy(oldBackground, 0, background, 0, row - 1);
			System.arraycopy(oldAlignment, 0, alignment, 0, row - 1);
		}
		System.arraycopy(oldSpan, 0, span, row, rows - row);
		System.arraycopy(oldEditable, 0, editable, row, rows - row);
		System.arraycopy(oldForeground, 0, foreground, row, rows - row);
		System.arraycopy(oldBackground, 0, background, row, rows - row);
		System.arraycopy(oldAlignment, 0, alignment, row, rows - row);

		for (int i = 0; i < columns; i++) {
			span[row][i][COLUMN] = 1;
			span[row][i][ROW] = 1;
		}
		
		rows++;
	}

	public boolean isVisible(int row, int column) {
		return !isOutOfBounds(row, column) && ((span[row][column][COLUMN] >= 1) || (span[row][column][ROW] >= 1));
	}

	private boolean isOutOfBounds(int row, int column) {
		return row < 0 || row >= span.length || column < 0
				|| column >= span[0].length;
	}

	public void setEditable(int row, int column, boolean editable) {
		this.editable[row][column] = editable;
	}
	
	public boolean isEditable(int row, int column) {
		return editable[row][column];
	}
	
	public void setCellForeground(int row, int column, Color foreground) {
		this.foreground[row][column] = foreground;
	}
	
	public Color getCellForeground(int row, int column) {
		return foreground[row][column];
	}
	
	public void setCellBackground(int row, int column, Color background) {
		this.background[row][column] = background;
	}
	
	public Color getCellBackground(int row, int column) {
		return background[row][column];
	}
	
	public void setNumberFormat(Class<? extends Number> clazz, NumberFormat numberFormat) {
		numberFormats.put(clazz, numberFormat);
	}
	
	public NumberFormat getNumberFormat(Class<?> clazz) {
		return numberFormats.get(clazz);
	}
	
	public void setCellAlignment(int row, int column, Integer alignment) {
		this.alignment[row][column] = alignment;
	}
	
	public Integer getCellAlignment(int row, int column) {
		return alignment[row][column];
	}
	
	public void setFractionColumn(int column, boolean fraction) {
		this.fraction[column] = fraction;
	}
	
	public boolean isFractionColumn(int column) {
		return fraction[column];
	}

}
